﻿// Connect.cpp : Implementation of CConnect
#include "stdafx.h"
#include "AddIn.h"
#include "XortSettingsDlg.h"
#include "Connect.h"

extern CAddInModule _AtlModule;


CConnect::CConnect()
{

}

CConnect::~CConnect()
{

}

HRESULT CConnect::FinalConstruct()
{
    return S_OK;
}

void CConnect::FinalRelease() 
{
}

// CConnect
STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication, ext_ConnectMode ConnectMode, IDispatch *pAddInInst, SAFEARRAY ** /*custom*/ )
{
	HRESULT hr = S_OK;
	pApplication->QueryInterface(__uuidof(DTE2), (LPVOID*)&m_pDTE);
	pAddInInst->QueryInterface(__uuidof(AddIn), (LPVOID*)&m_pAddInInstance);

    if( ConnectMode == ext_cm_Startup || ConnectMode == ext_cm_AfterStartup ) // this creates a temporary meny with 3 commands
    {
        CComVariant vEmpty( DISP_E_PARAMNOTFOUND, VT_ERROR );

        CComPtr<Commands> spCommands;
        hr = m_pDTE->get_Commands( &spCommands );
        CComQIPtr<Commands2> spCommands2( spCommands );

        if( FAILED( hr = spCommands2->AddNamedCommand2( m_pAddInInstance, 
            CComBSTR("XortCompile"), 
            CComBSTR("Compile"), 
            CComBSTR("Compile xort program"), 
            VARIANT_TRUE, CComVariant(2170), NULL, 
            vsCommandStatusSupported+vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &m_spCmdCompile ) ) )
        {
            return hr;
        }

        if( FAILED( hr = spCommands2->AddNamedCommand2( m_pAddInInstance, 
            CComBSTR("XortDebug"), 
            CComBSTR("Debug"), 
            CComBSTR("Debug xort program"), 
            VARIANT_TRUE, CComVariant(2997), NULL, 
            vsCommandStatusSupported+vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &m_spCmdDebug ) ) )
        {
            return hr;
        }

        if( FAILED( hr = spCommands2->AddNamedCommand2( m_pAddInInstance, 
            CComBSTR("XortSettings"), 
            CComBSTR("Setting"), 
            CComBSTR("Show Xort addin settings"), 
            VARIANT_TRUE, CComVariant(548), NULL, 
            vsCommandStatusSupported+vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &m_spCmdSettings ) ) )
        {
            return hr;
        }

        CComPtr<IDispatch> spDisp;
        m_pDTE->get_CommandBars( &spDisp );

        CComQIPtr<_CommandBars> spCommandBars( spDisp ); spDisp = NULL;
        CComPtr<CommandBar> spMenuBarCommandBar;
        //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
        if( SUCCEEDED( hr = spCommandBars->get_Item( CComVariant(L"MenuBar"), &spMenuBarCommandBar ) ) )
        {
            CComPtr<CommandBarControls> spMenuBarControls;
            if( SUCCEEDED( hr = spMenuBarCommandBar->get_Controls( &spMenuBarControls ) ) )
            {
                CComPtr<CommandBarControl> spXortMenu; // add the popup menu to the main menu bar
                if( SUCCEEDED( hr = spMenuBarControls->Add( CComVariant( msoControlPopup ), vEmpty, vEmpty, vEmpty, CComVariant( VARIANT_TRUE ), &spXortMenu ) ) )
                {
                    spXortMenu->get_Control( &spDisp );
                    CComQIPtr<CommandBarControl> spMenuControl( spDisp ); spDisp = NULL;
                    spMenuControl->put_Caption( CComBSTR( L"Xort" ) );

                    CComQIPtr<CommandBarPopup> spXortMenuPopup( spXortMenu );

                    CComPtr<CommandBar> spXortCommandBar;
                    spXortMenuPopup->get_CommandBar( &spXortCommandBar );

                    hr |= m_spCmdCompile->AddControl( spXortCommandBar, 1, &spDisp ); spDisp = NULL;
                    hr |= m_spCmdDebug->AddControl( spXortCommandBar, 2, &spDisp ); spDisp = NULL;
                    hr |= m_spCmdSettings->AddControl( spXortCommandBar, 3, &spDisp ); spDisp = NULL;

                    if( FAILED( hr ) )
                    {
                        hr = S_OK; // don't bore the IDE :)
                    }
                }
            }
        }
    }

	return hr;
}

STDMETHODIMP CConnect::OnDisconnection(ext_DisconnectMode /*RemoveMode*/, SAFEARRAY ** /*custom*/ )
{
    if( m_spCmdCompile )
    {
        m_spCmdCompile->Delete();
        m_spCmdCompile.Release();
    }

    if( m_spCmdDebug )
    {
        m_spCmdDebug->Delete();
        m_spCmdDebug.Release();
    }

    if( m_spCmdSettings )
    {
        m_spCmdSettings->Delete();
        m_spCmdSettings.Release();
    }

	m_pDTE = NULL;
	m_pAddInInstance = NULL;
	return S_OK;
}

STDMETHODIMP CConnect::OnAddInsUpdate( SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnStartupComplete( SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnBeginShutdown( SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::QueryStatus(BSTR bstrCmdName, vsCommandStatusTextWanted NeededText, vsCommandStatus *pStatusOption, VARIANT *pvarCommandText)
{
    if( NeededText == vsCommandStatusTextWantedNone )
    {
        if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortCompile") )
        {
            *pStatusOption = (vsCommandStatus)(vsCommandStatusEnabled+vsCommandStatusSupported);
        }
        else if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortDebug") )
        {
            *pStatusOption = (vsCommandStatus)(vsCommandStatusEnabled+vsCommandStatusSupported);
        }
        else if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortSettings") )
        {
            *pStatusOption = (vsCommandStatus)(vsCommandStatusEnabled+vsCommandStatusSupported);
        }
    }
    return S_OK;
}

STDMETHODIMP CConnect::Exec(BSTR bstrCmdName, vsCommandExecOption ExecuteOption, VARIANT * /*pvarVariantIn*/, VARIANT * /*pvarVariantOut*/, VARIANT_BOOL *pvbHandled)
{
	*pvbHandled = VARIANT_FALSE;
	if(ExecuteOption == vsCommandExecOptionDoDefault)
	{
		if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortCompile") )
		{
            CompileProject();
			*pvbHandled = VARIANT_TRUE;
		}
        else if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortDebug") )
        {
            CComBSTR bsDBG = m_SettingsDlg.GetDebuggerPath();
            LaunchDebugTarget( bsDBG );
            *pvbHandled = VARIANT_TRUE;
        }
        else if( !_wcsicmp(bstrCmdName, L"Xort.Connect.XortSettings") )
        {
            m_SettingsDlg.DoModal();
            *pvbHandled = VARIANT_TRUE;
        }
	}

	return S_OK;
}

void CConnect::CompileProject()
{
#if 0
    CString csXCL = m_SettingsDlg.GetCompilerPath();
    if( csXCL.IsEmpty() )
    {
        MessageBox( NULL, _T("Path to Xort compiler is not set. Please set it in settings page."), _T("Xort addin"), MB_OK );
        return;
    }

    std::vector<CComBSTR> vecFilesToCompile;

    CComPtr<_Solution> spSolution;
    HRESULT hr = m_pDTE->get_Solution( &spSolution );
    if( SUCCEEDED( hr ) )
    {
        CComPtr<Projects> spProjects;
        if( SUCCEEDED( hr = spSolution->get_Projects( &spProjects ) ) )
        {
            long lCount;
            spProjects->get_Count( &lCount );
            for( long i = 1; i <= lCount; i++ )
            {
                CComPtr<Project> spProject;
                if( SUCCEEDED( hr = spProjects->Item( CComVariant( i ), &spProject ) ) )
                {
                    CComPtr<ProjectItems> spProjectItems;
                    if( SUCCEEDED( hr = spProject->get_ProjectItems( &spProjectItems ) ) )
                    {
                        CollectFilesToCompile( vecFilesToCompile, spProjectItems );
                    }
                }
            }
        }
    }

    DWORD dwNumFiles = vecFilesToCompile.size();
    if( dwNumFiles > 0 )
    {
        CComBSTR bsXCL = csXCL;
        for( DWORD i = 0; i < dwNumFiles; i++ )
        {
            STARTUPINFOW sti;
            ZeroMemory( &sti, sizeof( sti ) );
            sti.cb = sizeof( STARTUPINFOW );
            
            PROCESS_INFORMATION pi;

            CComBSTR& bsTargetFile = vecFilesToCompile[i];
            CComBSTR bsCmdLine = L"\"";
            bsCmdLine += bsXCL;
            bsCmdLine += L"\" \"";
            bsCmdLine += bsTargetFile;
            bsCmdLine += L"\"";
            BOOL bOk = CreateProcessW( bsXCL, bsCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &sti, &pi );
            if( bOk )
            {
                WaitForSingleObject( pi.hProcess, INFINITE );
            }
        }
    }
#endif // 0
}

void CConnect::CollectFilesToCompile( std::vector<CComBSTR>& vecFiles, ProjectItems *pProjectItems )
{
    HRESULT hr;
    long lItemsCount = 0;
    pProjectItems->get_Count( &lItemsCount );

    for( long x = 1; x <= lItemsCount; x++ )
    {
        CComPtr<ProjectItem> spItem;
        if( SUCCEEDED( hr = pProjectItems->Item( CComVariant( x ), &spItem ) ) )
        {
            CComBSTR bsName;
            spItem->get_Name( &bsName );
            ATLTRACE( "Item name: %S\n", bsName );

            WCHAR *pSufix = wcsrchr( bsName.m_str, L'.' );
            if( pSufix && (
                wcscmp( pSufix, L".cpp" ) == 0 || wcscmp( pSufix, L".cx" ) == 0 || wcscmp( pSufix, L".xx" ) == 0
                wcscmp( pSufix, L".cxx" ) == 0 || wcscmp( pSufix, L".c"  ) == 0 || wcscmp( pSufix, L".x" ) == 0 ) )
            {
                CComBSTR bsFileName;
                hr = spItem->get_FileNames( 1, &bsFileName );
                ATLTRACE( "file: %S\n", bsFileName );

                VARIANT_BOOL bSaved = VARIANT_FALSE;
                hr = spItem->get_Saved( &bSaved );
                if( !bSaved )
                    hr = spItem->Save( NULL );

                vecFiles.push_back( bsFileName );
            }
            else
            {
                CComPtr<ProjectItems> spChildItems;
                if( SUCCEEDED( hr = spItem->get_ProjectItems( &spChildItems ) ) )
                {
                    CollectFilesToCompile( vecFiles, spChildItems );
                }
            }
        }
    }
}

HRESULT CConnect::LaunchDebugTarget( BSTR bsFileName )
{
    HRESULT hr;
    CComPtr<IServiceProvider> spSP;
    if( SUCCEEDED( hr = m_pDTE->QueryInterface( IID_IServiceProvider, (void**)&spSP ) ) )
    {
        CComPtr<IVsDebugger> spDbg;
        if( SUCCEEDED( hr = spSP->QueryService( SID_SVsShellDebugger, IID_IVsDebugger, (void**)&spDbg ) ) )
        {
            VsDebugTargetInfo *pDbgInfo = (VsDebugTargetInfo *)::CoTaskMemAlloc( sizeof(VsDebugTargetInfo) );
            if( pDbgInfo )
            {
                ZeroMemory( pDbgInfo, sizeof( VsDebugTargetInfo ) );

                CString csDbgPath = m_SettingsDlg.GetDebuggerPath();
                CString csCurDir = csDbgPath;
                int iPos = csCurDir.ReverseFind( '\\' );
                if( iPos != -1 )
                    csCurDir.Truncate( iPos );

                // get path to our dummy process
                TCHAR szXDummy[_MAX_PATH];
                GetModuleFileName( _AtlModule.GetResourceInstance(), szXDummy, _MAX_PATH );
                TCHAR *p = _tcsrchr( szXDummy, _T('\\') );
                if( p )
                    _tcscpy( p, _T("\\XDbgDummyProcess.exe") );

                CString csCmdLine = m_SettingsDlg.GetProgramPath();
                csCmdLine += _T(" ");
                csCmdLine += m_SettingsDlg.GetArguments();

                pDbgInfo->cbSize            = sizeof( VsDebugTargetInfo );
                pDbgInfo->dlo               = DLO_CreateProcess;     // yes, create the debugger process
                pDbgInfo->bstrExe           = CComBSTR( szXDummy ).Detach();  // actually we launch dummy process under our DE
                pDbgInfo->bstrCurDir        = CComBSTR( csCurDir ).Detach();  // set current directory to debugger dir
                pDbgInfo->bstrArg           = CComBSTR( csCmdLine ).Detach(); // arguments passed to IDebugEngine2::LaunchSuspended
                pDbgInfo->bstrRemoteMachine = NULL;                  // debug locally
                pDbgInfo->fSendStdoutToOutputWindow = 0;             // let stdout stay with the application.
                pDbgInfo->clsidCustom       = IID_IXortDebugEngine;  // set the launching debug engine guid
                pDbgInfo->grfLaunch         = 0;                     // DBGLAUNCH_WaitForAttachComplete ? DBGLAUNCH_Selected;

                try
                {
                    if( FAILED( hr = spDbg->LaunchDebugTargets( 1, pDbgInfo ) ) )
                    {
                        HRESULT hrLaunch = hr;
                        ATLTRACE( "LaunchDebugTargets FAILED: 0x%08X\n", hrLaunch );
                        CComPtr<IVsUIShell> spShell;
                        if( SUCCEEDED( hr = spSP->QueryService( SID_SVsUIShell, IID_IVsUIShell, (void**)&spShell ) ) )
                        {
                            CComBSTR bsError;
                            spShell->GetErrorInfo( &bsError );
                            ATLTRACE( "Error: %S\n", bsError );
                            spShell->ReportErrorInfo( hrLaunch );
                        }
                    }

                    //::CoTaskMemFree( pDbgInfo ); NO, this memory is now belongs to IDE, do not free it
                }
                catch( ... )
                {
                    ::CoTaskMemFree( pDbgInfo );
                }
            }
        }
    }
    return hr;
}
